home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d16 / winvn060.arc / WVGROUP.C < prev    next >
C/C++ Source or Header  |  1991-07-01  |  30KB  |  853 lines

  1. /*---  wvgroup.c ------------------------------------------- */
  2. /*  This file contains the window procedure for the Group Viewing window
  3.  *  for WinVN.
  4.  */
  5.  
  6. #include "windows.h"
  7. #ifndef MAC
  8. #include "winundoc.h"
  9. #endif
  10. #include "WVglob.h"
  11. #include "WinVn.h"
  12. #ifdef MAC
  13. #include "MRRM1.h"
  14. #include <DialogMgr.h>
  15. #endif
  16.  
  17. /*--- FUNCTION: WinVnViewWndProc --------------------------------------------
  18.  *
  19.  *    Window procedure for a Group window, which contains the subjects
  20.  *    of the various articles in a newsgroup.
  21.  *    Note that there may be several different Group windows active;
  22.  *    this routine gets called any time anything happens to any of them.
  23.  */
  24.  
  25. long FAR PASCAL WinVnViewWndProc(hWnd, message, wParam, lParam)
  26. HWND hWnd;
  27. unsigned message;
  28. WORD wParam;
  29. LONG lParam;
  30. {
  31.    FARPROC lpProcAbout;
  32.    HMENU hMenu;
  33.  
  34.    PAINTSTRUCT ps;                              /* paint structure          */
  35.  
  36.    HDC hDC;                                    /* handle to display context */
  37.    RECT clientRect;                                  /* selection rectangle      */
  38.    HDC  hDCView;
  39.    TypDoc *ThisDoc;
  40.    int ih,j;
  41.    int iart;
  42.    int found;
  43.    int imemo;
  44.    int CtrlState;
  45.    TypBlock far *BlockPtr, far *ArtBlockPtr;
  46.    TypLine far *LinePtr, far *ArtLinePtr;
  47.    HANDLE hBlock;
  48.    unsigned int Offset;
  49.    char mybuf[MAXINTERNALLINE];
  50.    TypDoc *MyDoc;
  51.    TypLineID MyLineID;
  52.    POINT ptCursor;
  53.  
  54.    int X, Y, nWidth, nHeight;
  55.    int mylen;
  56.    int OldSel = FALSE;
  57.  
  58.    /* We know what *window* is being acted on, but we must find
  59.     * out which *document* is being acted on.  There's a one-to-one
  60.     * relationship between the two, and we find out which document
  61.     * corresponds to this window by scanning the GroupDocs array.
  62.     */
  63.  
  64.    for(ih=0,found=FALSE; !found && ih<MAXGROUPWNDS; ih++) {
  65.       if(GroupDocs[ih].hDocWnd == hWnd) {
  66.          found = TRUE;
  67.          ThisDoc = &(GroupDocs[ih]);
  68.       }
  69.    }
  70.  
  71.    if(!found) {
  72.       ThisDoc = CommDoc;
  73.    }
  74.  
  75.     switch (message) {
  76.         case WM_ACTIVATE:
  77.             if(wParam){
  78.                ActiveGroupDoc = ThisDoc;               
  79. #if 0
  80.                SetMenuBar(groupMenuBar);
  81.                DrawMenuBar();
  82. #endif
  83.             }
  84.             /* fall through */
  85.         case WM_SYSCOMMAND:
  86.            return (DefWindowProc(hWnd, message, wParam, lParam));
  87.  
  88.         case WM_SIZE:
  89.             GetClientRect(hWnd, &clientRect);
  90.             ThisDoc->ScXWidth =  clientRect.right;
  91.             ThisDoc->ScYHeight = clientRect.bottom;
  92.             ThisDoc->ScYLines = (clientRect.bottom - clientRect.top - TopSpace) / LineHeight;
  93.             ThisDoc->ScXChars = (clientRect.right - clientRect.left - SideSpace) / CharWidth;
  94.             break;
  95.  
  96.          case WM_DESTROY:
  97.             /* Unlink all the article windows that belong to this group */
  98.  
  99.             UpdateSeenArts(ThisDoc);
  100.             UnlinkArtsInGroup(ThisDoc);
  101.             ThisDoc->InUse = FALSE;
  102.             if(ThisDoc == CommDoc) {
  103.                CommBusy = FALSE;
  104.                CommDoc = (TypDoc *) NULL;
  105.             }
  106.             /* Clear the pointer in the line for this group in the   */
  107.             /* NetDoc document.  This pointer currently points       */
  108.             /* to the current document, which we are wiping out      */
  109.             /* with the destruction of this window.                  */
  110.  
  111.             LockLine(ThisDoc->hParentBlock,ThisDoc->ParentOffset,ThisDoc->ParentLineID,&BlockPtr,&LinePtr);
  112.             ( (TypGroup far *)
  113.               ( ((char far *)LinePtr)+sizeof(TypLine) ) )->SubjDoc = (TypDoc *)NULL;
  114.             UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  115.             /* Clear document                                        */
  116.             FreeDoc(ThisDoc);
  117.  
  118.             /* If there's another group window, make it the active   */
  119.             /* group window so we don't create a new one if the      */
  120.             /* New Group flag is FALSE.                              */
  121.  
  122.             for(j=MAXGROUPWNDS-1; j>=0; j--) {
  123.                if(GroupDocs[j].InUse) {
  124.                   ActiveGroupDoc = &(GroupDocs[j]);
  125.                   break;
  126.                }
  127.             }
  128.             break;
  129.  
  130.          case WM_KEYDOWN:
  131.             /* See if this key should be mapped to a scrolling event
  132.              * for which we have programmed the mouse.  If so,
  133.              * construct the appropriate mouse call and call the mouse code.
  134.              */
  135. #ifndef MAC
  136.             if(wParam == VK_F6) {
  137.                NextWindow(ThisDoc);
  138.             } else {
  139.                CtrlState = GetKeyState(VK_CONTROL) < 0;
  140.                for(j=0; j<NUMKEYS; j++) {
  141.                   if(wParam == key2scroll[j].wVirtKey &&
  142.                    CtrlState == key2scroll[j].CtlState) {
  143.                      SendMessage(hWnd,key2scroll[j].iMessage,
  144.                       key2scroll[j].wRequest,0L);
  145.                      break;
  146.                   }
  147.                }
  148.             }
  149. #endif
  150.             break;
  151.  
  152.  
  153.         case WM_CHAR:
  154.             /* Carriage Return means the same as double-clicking
  155.              * on where the cursor is currently pointing.
  156.              */
  157.             if (wParam == '\r') {
  158.                 GetCursorPos(&ptCursor);
  159.                 ScreenToClient(hWnd, &ptCursor);
  160. #ifdef MAC
  161.             X = ptCursor.h;
  162.             Y = ptCursor.v;
  163. #else
  164.                 X = ptCursor.x;
  165.                 Y = ptCursor.y;
  166. #endif
  167.                 goto getarticle;
  168.             } else {
  169.             }
  170.             break;
  171.  
  172.          case WM_LBUTTONDOWN:
  173.           /*  Clicking the left button on an article name toggles the
  174.            *  selected/not selected status of that article.
  175.            *  Currently selected articles are displayed in reverse video.
  176.            */
  177. #if 0
  178.           /****  This code is currently disabled.  */
  179.             X = LOWORD(lParam);
  180.             Y = HIWORD(lParam);
  181.             if(CursorToTextLine(X,Y,ThisDoc,&BlockPtr,&LinePtr)) {
  182.                ( (TypArticle far *)( ((char far *)LinePtr) + sizeof(TypLine) ) )
  183.                 ->Selected ^= TRUE;
  184.                GlobalUnlock(BlockPtr->hCurBlock);
  185.             }
  186.  
  187.             InvalidateRect(hWnd, NULL, FALSE);
  188. #endif
  189.             break;
  190.  
  191.         case WM_LBUTTONDBLCLK:
  192.            /*  Double-clicking on an article subject creates an "Article"
  193.             *  window, whose purpose is to display the article.
  194.             */
  195.             X = LOWORD(lParam);
  196.             Y = HIWORD(lParam);
  197.            getarticle:;
  198.  
  199.             if(CursorToTextLine(X,Y,ThisDoc,&ArtBlockPtr,&ArtLinePtr)) {
  200.                ViewArticle(ThisDoc,ArtBlockPtr,ArtLinePtr,FALSE);
  201.             }
  202.  
  203.             break;
  204.  
  205.         case WM_VSCROLL:
  206.             ScrollIt(ThisDoc,wParam,lParam);
  207.             break;
  208.  
  209.          case WM_PAINT:
  210.           {
  211.             HANDLE hBlock;
  212.             int MyLen, width;
  213.             unsigned int Offset;
  214.             int VertLines, HorzChars;
  215.             int EndofDoc = FALSE;
  216.             int RangeHigh, CurPos;
  217.             int RestX,indicatorwidth,Xtext;
  218.             char far *textptr;
  219.             char *indcptr;
  220.             TypArticle far *MyArt;
  221.             TypBlock far *BlockPtr;
  222.             TypLine far *LinePtr;
  223.             TypBlock far *NetBlockPtr;
  224.             TypLine far *NetLinePtr;
  225.             TypGroup far *group;
  226.             long int OldHighestSeen;
  227.             HANDLE hBlackBrush;
  228.             DWORD MyColors[4], MyBack[4];
  229. #ifdef MAC
  230.             RECT myRect;
  231.             POINT myPoint;
  232. #endif
  233.             DWORD Rop;
  234.             int MyColorMask = 0;
  235.             int PrevColorMask = MyColorMask;
  236.  
  237.             /* MyColors and MyBack are arrays of colors used to display text
  238.              * foreground and background.
  239.              * The ColorMask variables are indices into these arrays.
  240.              * We set and clear bits in these indices depending upon
  241.              * whether the article has been selected or seen.
  242.              */
  243.  
  244. #define SEEN_MASK 1
  245. #define SELECT_MASK 2
  246.             hDC = BeginPaint (hWnd, &ps);
  247.  
  248.             GetClientRect(hWnd, &clientRect);
  249.             SelectObject(hDC,hFont);
  250.             VertLines = ThisDoc->ScYLines;
  251.             HorzChars = ThisDoc->ScXChars;
  252.  
  253.             MyColors[0] = GetTextColor(hDC);  /* black */
  254.             MyColors[1] = GroupSeenColor;
  255.             MyColors[2] = GetBkColor(hDC);    /* white */
  256.             MyColors[3] = MyColors[1];        /* blue  */
  257.  
  258.             MyBack[0] = MyColors[2];          /* white */
  259.             MyBack[1] = MyColors[2];
  260.             MyBack[2] = MyColors[0];
  261.             MyBack[3] = MyColors[0];
  262.  
  263.             LockLine(ThisDoc->hCurTopScBlock,ThisDoc->TopScOffset,ThisDoc->TopScLineID,
  264.              &BlockPtr,&LinePtr);
  265.  
  266.             /* Update the scroll bar thumb position.                 */
  267.  
  268.             CurPos = ThisDoc->TopLineOrd;
  269.             if(CurPos<0) CurPos = 0;
  270.             RangeHigh = ThisDoc->TotalLines - VertLines;
  271.             if(RangeHigh<0) RangeHigh = 0;
  272.             SetScrollRange(hWnd,SB_VERT,0,RangeHigh,FALSE);
  273.             SetScrollPos  (hWnd,SB_VERT,CurPos,TRUE);
  274.  
  275.             indicatorwidth = LOWORD(GetTextExtent(hDC,"s",2)) * 8 / 7;
  276.             LockLine(ThisDoc->hParentBlock,ThisDoc->ParentOffset,ThisDoc->ParentLineID,
  277.               &NetBlockPtr, &NetLinePtr);
  278.             group = (TypGroup far *) ((char far *)NetLinePtr + sizeof(TypLine));
  279.             OldHighestSeen = group->HighestPrevSeen;
  280.             UnlockLine(NetBlockPtr,NetLinePtr,&hBlock,&Offset,&MyLineID);
  281.  
  282. #ifdef MAC
  283.             myRect.right = ThisDoc->DocClipRect.right;
  284.             myRect.top = 0;
  285.             myRect.bottom = LineHeight;
  286. #endif
  287.  
  288.             /* Now paint this stuff on the screen.               */
  289.  
  290.             X = SideSpace;
  291.             Xtext = X + indicatorwidth;
  292.             Y = StartPen;
  293.  
  294.             if(LinePtr->length != END_OF_BLOCK)
  295.             do {
  296.                MyArt =  ((TypArticle far *)
  297.                 ((char far *)LinePtr + sizeof(TypLine)));
  298.                MyLen = LinePtr->length - sizeof(TypLine) -
  299.                 sizeof(TypArticle) - sizeof(int) - 1;
  300.                MyLen = MyArt->NameLen;
  301.                textptr = (char far *) LinePtr + sizeof(TypLine) +
  302.                 sizeof(TypArticle);
  303.  
  304.                /* Figure out the color of this line.                 */
  305.  
  306.                if(MyArt->Seen) {
  307.                   MyColorMask |= SEEN_MASK;
  308.                } else {
  309.                   MyColorMask &= (0xff - SEEN_MASK);
  310.                }
  311.                if(MyArt->Selected) {
  312.                   MyColorMask |= SELECT_MASK;
  313.                   Rop = BLACKNESS;
  314.                } else {
  315.                   MyColorMask &= 0xff - SELECT_MASK;
  316.                   Rop = WHITENESS;
  317.                }
  318.                if(MyColorMask != PrevColorMask) {
  319.                   SetTextColor(hDC,MyColors[MyColorMask]);
  320.                   SetBkColor(hDC,MyBack[MyColorMask]);
  321.                   PrevColorMask = MyColorMask;
  322.                }
  323.  
  324.                /* Figure out what indicator character to use. */
  325.  
  326.                indcptr = "    ";
  327.                if(ThisDoc->FindLineID == LinePtr->LineID) {
  328.                   indcptr = ">   ";
  329.                } else if(MyArt->Seen) {
  330.                   indcptr = "s   ";
  331.                } else if(OldHighestSeen) {
  332.                   if(MyArt->Number > OldHighestSeen) {
  333.                      indcptr = "n   ";
  334.                   }
  335.                }
  336.  
  337.                /* Now write out the line.                            */
  338.  
  339.                TextOut(hDC,X,Y,indcptr,4);
  340.                width = LOWORD(GetTextExtent(hDC,textptr,MyLen));
  341.                TextOut(hDC,Xtext,Y,textptr,MyLen);
  342. #ifdef MAC
  343.                GetPen(&myPoint);
  344.                myRect.left = myPoint.h;
  345.                FillRect(&myRect,white);
  346.  
  347.                myRect.top += LineHeight;
  348.                myRect.bottom += LineHeight;
  349. #else
  350.                RestX = Xtext + width;
  351.                PatBlt(hDC,RestX,Y,clientRect.right-RestX,LineHeight,Rop);
  352. #endif
  353. #if 0
  354.                if(MyLen < HorzChars) {
  355.                   RestX = X + width;
  356.             /*    TextOut(hDC,RestX,Y,Blanks,MAXINTERNALLINE); */
  357.                   PatBlt(hDC,RestX,Y,clientRect.right-RestX,LineHeight,Rop);
  358.                }
  359. #endif
  360.                Y += LineHeight;
  361.             } while(--VertLines>0 && NextLine(&BlockPtr,&LinePtr) );
  362.  
  363.             /* We've reached the end of the data to be displayed     */
  364.             /* on this window.  If there's more screen real estate   */
  365.             /* left, just blank it out.                              */
  366.  
  367.             SetTextColor(hDC,MyColors[0]);
  368.             SetBkColor(hDC,MyBack[0]);
  369. #ifndef MAC
  370.             PatBlt(hDC,0,Y,clientRect.right,clientRect.bottom-Y,PATCOPY);
  371. #else
  372.             myRect.bottom = ThisDoc->DocClipRect.bottom;
  373.             myRect.left = 0;
  374.             EraseRect(& myRect);
  375. #endif
  376.             UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  377.             EndPaint(hWnd, &ps);
  378.             break;
  379.          }
  380.  
  381.         case WM_COMMAND:
  382.             switch(wParam) {
  383.                 case IDV_EXIT:
  384.                     DestroyWindow(hWnd);
  385.                     break;
  386.  
  387.                 case IDV_NEXT:
  388.                      break;
  389.  
  390.                case IDM_FIND:
  391.                   FindDoc = ThisDoc;
  392. #ifndef MAC
  393.                   if(DialogBox(hInst,"WinVnFind",hWnd,lpfnWinVnFindDlg)) {
  394.                      found = DoFind(TRUE);
  395.                      if(!found) {
  396.                         strcpy(mybuf,"\"");
  397.                         strcat(mybuf,ThisDoc->SearchStr);
  398.                         strcat(mybuf,"\" not found.");
  399.                         MessageBox(hWnd,mybuf,"Not found",MB_OK);
  400.                      }
  401.                   }
  402. #else
  403.                   {
  404.                   DialogPtr myDialog;
  405.                   BOOL dialogDone = FALSE, oktofind = FALSE;
  406.                   int itemhit;
  407.                   int itemType;
  408.                   Handle itemHandle;
  409.                   Rect itemRect;
  410.                   char myStr[255];
  411.  
  412.                   myDialog = GetNewDialog(DLOG_FIND,NULL,(WindowPtr)-1);
  413.                   GetDItem(myDialog,3,&itemType,&itemHandle,&itemRect);
  414.                   strcpy(myStr,FindDoc->SearchStr);
  415.                   CtoPstr(myStr);
  416.                   SetIText(itemHandle,myStr);
  417.                   ShowWindow((WindowPtr)myDialog);
  418.                   while(!dialogDone) {
  419.                      ModalDialog(NULL,&itemhit);
  420.                      switch(itemhit) {
  421.                         case OK:
  422.                            dialogDone = TRUE;
  423.                            oktofind = TRUE;
  424.                            break;
  425.                         case Cancel:
  426.                            dialogDone = TRUE;
  427.                            break;
  428.                      }
  429.                   }
  430.                   HideWindow(myDialog);
  431.                   if(oktofind) {
  432.                      GetIText(itemHandle,&myStr);
  433.                      PtoCstr(myStr);
  434.                      strcpy(FindDoc->SearchStr,(char *)myStr);
  435.                      found = DoFind(TRUE);
  436.                      if(!found) {
  437.                         strcpy(mybuf,"\"");
  438.                         strcat(mybuf,ThisDoc->SearchStr);
  439.                         strcat(mybuf,"\" not found.");
  440.                         MessageBox(hWnd,mybuf,"Not found",MB_OK);
  441.                      }
  442.                   }
  443.                   }
  444. #endif
  445.                   break;
  446.  
  447.                case IDM_FIND_NEXT_SAME:
  448.                   FindDoc = ThisDoc;
  449.                   if(strcmp(FindDoc->SearchStr,"")) {
  450.                      found = DoFind(FALSE);
  451.                      if(!found) {
  452.                         strcpy(mybuf,"\"");
  453.                         strcat(mybuf,ThisDoc->SearchStr);
  454.                         strcat(mybuf,"\" not found.");
  455.                         MessageBox(hWnd,mybuf,"No more occurrences",MB_OK);
  456.                      }
  457.                   }
  458.                   break;
  459.  
  460.                case IDV_CREATE:
  461.                   /* We are creating the skeleton text of a new posting.
  462.                    * Most of the work is done by CreatePostingWnd and
  463.                    * CreatePostingText.  Here we have to identify
  464.                    * the newsgroup for those routines.
  465.                    * Get the newsgroup from the line in NetDoc that
  466.                    * points to this document.
  467.                    */
  468.                   LockLine(ThisDoc->hParentBlock,ThisDoc->ParentOffset,
  469.                    ThisDoc->ParentLineID,&BlockPtr,&LinePtr);
  470.                   ExtractTextLine(ThisDoc->ParentDoc,LinePtr,
  471.                    mybuf,MAXINTERNALLINE);
  472.                   UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  473.                   NewsgroupsPtr = mybuf;
  474.                   CreatePostingWnd((TypDoc *)NULL,DOCTYPE_POSTING);
  475.                   break;
  476.             }
  477.             break;
  478.  
  479.  
  480.         default:
  481.             return (DefWindowProc(hWnd, message, wParam, lParam));
  482.     }
  483.     return (0);
  484. }
  485.  
  486.  
  487. /* --- function CopyNonBlank ----------------------------------------
  488.  *
  489.  *    Copy a string, terminating at the first blank or zero byte.
  490.  *
  491.  *    Entry    strtarg     FWA of target area.
  492.  *             strsource   FWA of source string.
  493.  *             maxchars    the maximum number of characters to copy
  494.  *                         if no blank or zero is encountered.
  495.  *
  496.  *    Exit     returns the number of characters copied.
  497.  *
  498.  *    I believe this routine is no longer used, but I'm leaving it here.
  499.  */
  500.  
  501. int
  502. CopyNonBlank(strtarg,strsource,maxchars)
  503. char *strtarg,*strsource;
  504. int maxchars;
  505. {
  506.     int j;
  507.  
  508.     for(j=0; j<maxchars && strsource[j] != ' '; j++) strtarg[j] = strsource[j];
  509.     return(j);
  510. }
  511.  
  512. /* --- function FileLength -------------------------------------------
  513.  *
  514.  *    Find the size, in bytes, of a file.
  515.  *
  516.  *    Entry    hFile    handle of the file in question.
  517.  *
  518.  *    Exit     returns the length of the file in bytes.
  519.  *
  520.  *    This routine is no longer used.
  521.  */
  522.  
  523. long
  524. FileLength(hFile)
  525. HANDLE hFile;
  526. {
  527.     long lCurrentPos = _llseek(hFile, 0L, 1);
  528.     long lFileLength = _llseek(hFile, 0L, 2);
  529.  
  530.     _llseek(hFile, lCurrentPos, 0);
  531.  
  532.     return lFileLength;
  533.  
  534. }
  535.  
  536. /*-- function ViewArticle -------------------------------------------------
  537.  *
  538.  *  View a given article.   Either create a new window for it or
  539.  *  recycle an existing window.
  540.  *  This function requests an article from the server, so there
  541.  *  must not already be a transaction in progress.
  542.  *
  543.  *    Entry    Doc            points to the document for this group.
  544.  *             ArtBlockPtr &
  545.  *             ArtLinePtr     point to the line in the group document
  546.  *                            that has the subject for this article.
  547.  *             Reuse          is TRUE if we ought to reuse the
  548.  *                            currently active article window (if any).
  549.  */
  550. void
  551. ViewArticle(Doc,ArtBlockPtr,ArtLinePtr,Reuse)
  552. TypDoc *Doc;
  553. TypBlock far *ArtBlockPtr;
  554. TypLine far  *ArtLinePtr;
  555. BOOL Reuse;
  556. {
  557.    TypDoc *MyDoc;
  558.    TypDoc *GroupDoc;
  559.    BOOL newdoc;
  560.    BOOL found;
  561.    int docnum;
  562.    HANDLE hBlock;
  563.    unsigned int Offset;
  564.    TypLineID MyLineID;
  565.    TypBlock far *BlockPtr;
  566.    TypLine far *LinePtr;
  567.    HWND hWndArt;
  568.    int width;
  569.    char mybuf[MAXINTERNALLINE];
  570.    long int artnum;
  571.    char far *lpsz;
  572.    HWND hWndGroup = Doc->hDocWnd;
  573.    char far *lpszGroupName;
  574.  
  575.    if(MyDoc = (( (TypArticle far *)
  576.      ( ((char far *)ArtLinePtr)+sizeof(TypLine) ) )->ArtDoc)) {
  577.  
  578.    /* We already have a document containing the article */
  579.    /* so just activate it.                */
  580.  
  581.   /*  ShowWindow(MyDoc->hDocWnd,SW_SHOW); */
  582.       SetActiveWindow(MyDoc->hDocWnd);
  583.       SetFocus(MyDoc->hDocWnd);
  584.       GlobalUnlock(ArtBlockPtr->hCurBlock);
  585.       goto endit;
  586.    }
  587.    if(CommBusy) {
  588.       GlobalUnlock(ArtBlockPtr->hCurBlock);
  589.       MessageBox(hWndGroup,"Sorry, I am already busy retrieving information from the server.\n\
  590. Try again in a little while.","Can't request text of article",MB_OK|MB_ICONASTERISK);
  591.       goto endit;
  592.    }
  593.  
  594.    newdoc = FALSE;
  595.    if((NewArticleWindow && !Reuse) || !ActiveArticleDoc || !(ActiveArticleDoc->InUse)) {
  596.       found = FALSE;
  597.       for(docnum=0; docnum<MAXARTICLEWNDS; docnum++) {
  598.         if(!ArticleDocs[docnum].InUse) {
  599.             found = TRUE;
  600.             newdoc = TRUE;
  601.             CommDoc = &(ArticleDocs[docnum]);
  602.             break;
  603.          }
  604.       }
  605.       if(!found) {
  606.          MessageBox(hWndGroup,"You have too many article windows \
  607. active;\nClose one or uncheck the option \"New Window for each Article\".","Can't open new window",MB_OK|MB_ICONASTERISK);
  608.          UnlockLine(ArtBlockPtr,ArtLinePtr,&hBlock,&Offset,&MyLineID);
  609.          goto endit;
  610.       }
  611.    } else {
  612.       /* Must reuse old window for this article.         */
  613.       CommDoc = ActiveArticleDoc;
  614.       if(CommDoc->hParentBlock) {
  615.          LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  616.          ( (TypArticle far *)
  617.           ( ((char far *)LinePtr)+sizeof(TypLine) ) )->ArtDoc = (TypDoc *)NULL;
  618.          UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  619.       }
  620.       /* clear out old doc */
  621.       FreeDoc(CommDoc);
  622.    }
  623.  
  624.    ( (TypArticle far *)( ((char far *)ArtLinePtr) + sizeof(TypLine) ) )
  625.     ->Seen = TRUE;
  626.    InvalidateRect(hWndGroup,NULL,FALSE);
  627.  
  628.    lpsz = (char far *) ( ((char far *)ArtLinePtr) +
  629.     sizeof(TypLine)+ sizeof(TypArticle) ) ;
  630.    strcpy(mybuf,"Retrieving \"");
  631.    lstrcat(mybuf,lpsz);
  632.    lstrcat(mybuf,"\"");
  633.  
  634.    if(newdoc) {
  635.       if(xScreen > 88*CharWidth) {
  636.          width = 88*CharWidth;
  637.       } else {
  638.          width = xScreen - 1*CharWidth;
  639.       }
  640.       hWndArt = CreateWindow("WinVnArt",
  641.       mybuf,
  642.       WS_OVERLAPPEDWINDOW | WS_VSCROLL  ,
  643.       (docnum)*CharWidth,     /* Initial X pos */
  644.       (int) (yScreen*3/8) + (docnum)*LineHeight, /* Initial Y pos */
  645.       (int) width,                     /* Initial X Width */
  646.       (int) (yScreen*5/8) - (1*LineHeight),  /* Initial Y height */
  647.       NULL,
  648.       NULL,
  649.       hInst,
  650.       NULL);
  651.  
  652.       if (!hWndArt) return;  /* ??? */
  653. #ifndef MAC
  654.       ShowWindow(hWndArt, SW_SHOWNORMAL);
  655. #else
  656.       MyShowWindow(hWndArt, SW_SHOWNORMAL);
  657. #endif
  658.    } else {
  659.       hWndArt = CommDoc->hDocWnd;
  660.       SetWindowText(hWndArt,mybuf);
  661.    }
  662.  
  663.    /*  Now that we have created the window, create the corresponding
  664.     *  document, and make the new window active.
  665.     */
  666.  
  667.    InitDoc(CommDoc,hWndArt,ArtBlockPtr->OwnerDoc,DOCTYPE_ARTICLE);
  668.    CommDoc->InUse = TRUE;
  669.    PtrToOffset(ArtBlockPtr,ArtLinePtr,&(CommDoc->hParentBlock),
  670.     &(CommDoc->ParentOffset),&(CommDoc->ParentLineID));
  671.    ArtBlockPtr->OwnerDoc->hLastSeenBlock = CommDoc->hParentBlock;
  672.    ArtBlockPtr->OwnerDoc->LastSeenOffset = CommDoc->ParentOffset;
  673.    SetActiveWindow(hWndArt);
  674.    SetFocus(hWndArt);
  675.  
  676.    ( (TypArticle far *)( ((char far *)ArtLinePtr) + sizeof(TypLine) ) )
  677.     ->ArtDoc = CommDoc;
  678.    InvalidateRect(hWndArt,NULL,FALSE);
  679.    UpdateWindow(hWndArt);
  680.  
  681.    CommLinePtr = CommLineIn;
  682.    CommBusy = TRUE;
  683.    CommState = ST_ARTICLE_RESP;
  684.                
  685.                
  686.    /* If we're not already in this group on the server,
  687.     * send out a GROUP command for this window so we get back
  688.     * into the right Group.
  689.     */
  690.    LockLine(Doc->hParentBlock,Doc->ParentOffset,Doc->ParentLineID,
  691.        &BlockPtr,&LinePtr);
  692.    lpszGroupName = (char far *)LinePtr + sizeof(TypLine) + sizeof(TypGroup);
  693.    if(lstrcmp(CurrentGroup,lpszGroupName)) {     
  694.       CommState = ST_GROUP_REJOIN;
  695.       strcpy(mybuf,"GROUP ");
  696.       lstrcat(mybuf,lpszGroupName);
  697.       /* lstrcat(mybuf,"\r"); */
  698.       mylstrncpy(CurrentGroup,lpsz,MAXGROUPNAME);
  699.       PutCommLine(mybuf,lstrlen(mybuf));
  700.    }       
  701.    UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  702.  
  703.    artnum = ((TypArticle far *)((char far *)ArtLinePtr +
  704.     sizeof(TypLine)))->Number;
  705.    sprintf(mybuf,"ARTICLE %ld\r",artnum);
  706.    PutCommLine(mybuf,lstrlen(mybuf));
  707.  
  708.    GlobalUnlock(ArtBlockPtr->hCurBlock);
  709. endit:;
  710. }
  711.  
  712. /*-- Function UnlinkArtsInGroup ---------------------------------------
  713.  *
  714.  *  Modify all the article documents and all the article windows currently
  715.  *  associated with a group so that none of them points to that group.
  716.  *  Used when the group window is going away or is being recycled.
  717.  *
  718.  *    Entry    GroupDoc    points to the document to which references
  719.  *                         should be eliminated.
  720.  */
  721. void
  722. UnlinkArtsInGroup(GroupDoc)
  723. TypDoc *GroupDoc;
  724. {
  725.    int iart;
  726.  
  727.    for(iart=0; iart<MAXARTICLEWNDS; iart++ ) {
  728.       if(ArticleDocs[iart].InUse && ArticleDocs[iart].ParentDoc == GroupDoc) {
  729.          ArticleDocs[iart].ParentDoc = (TypDoc *)NULL;
  730.          ArticleDocs[iart].hParentBlock = 0;
  731.       }
  732.    }
  733. }
  734.  
  735. /*--- function UpdateSeenArts -------------------------------------------
  736.  *
  737.  *  Given a Group document, update the TypGroup line for
  738.  *  that document in the Net document with respect to which
  739.  *  articles have been seen.
  740.  *  This routine would typically be called just before a Group document
  741.  *  is going to be destroyed or erased.  That would be the time to
  742.  *  take the information in the TypArticle structures of each line
  743.  *  in the document and transfer it to the line in the NetDoc document
  744.  *  corresponding to this group.
  745.  *
  746.  *  This routine has to take information of the form:
  747.  *    123:Unseen;  124:Seen; 125:Unseen; 126:Unseen; 127:Seen; 128:Seen; 129:Seen
  748.  *  found in the TypArticle structures in consecutive lines in the document
  749.  *  and transform it to the general form used by .newsrc files:
  750.  *    124,127-129
  751.  *  (though we are using our internal representation & not ASCII characters).
  752.  *
  753.  *    Entry    Doc      points to the document for this group.
  754.  *
  755.  *    Exit     The line in the Net document corresponding to this
  756.  *              group has been updated.
  757.  */
  758. void
  759. UpdateSeenArts(Doc)
  760. TypDoc *Doc;
  761. {
  762.    TypRange MyRange, *RangePtr;
  763.    TypGroup *group;
  764.    TypLine far *LinePtr, far *ParentLine;
  765.    TypBlock far *BlockPtr, far *ParentBlock;
  766.    HANDLE hLine;
  767.    TypLine *LocalLinePtr;
  768.    TypArticle far *Art;
  769.    BOOL InSeen = TRUE;
  770.    unsigned int MyLength;
  771.    unsigned int maxRanges;
  772.  
  773.    /*  Get the line in the Net document that corresponds to this
  774.     *  group.  Make a local copy of it and set RangePtr to point to
  775.     *  the first range in that line.  We will ignore the old line's
  776.     *  "seen" data and create the information afresh from what we
  777.     *  have in this document.
  778.     */
  779.    LockLine(Doc->hParentBlock,Doc->ParentOffset,Doc->ParentLineID,
  780.      &ParentBlock, &ParentLine);
  781.  
  782.    hLine = LocalAlloc(LMEM_MOVEABLE,BLOCK_SIZE);
  783.    LocalLinePtr = (TypLine *) LocalLock(hLine);
  784.    group = (TypGroup *) ((char *) LocalLinePtr + sizeof(TypLine));
  785.  
  786.    MoveBytes(ParentLine,LocalLinePtr,ParentLine->length);
  787.    group->nRanges = 0;
  788.    maxRanges = ((Doc->BlockSize - Doc->SplitSize) - ParentLine->length +
  789.       group->nRanges * sizeof(TypRange)) / sizeof(TypRange) - 1;
  790.  
  791.    RangePtr = (TypRange *) ((char *) LocalLinePtr + sizeof(TypLine) +
  792.     RangeOffset(group->NameLen));
  793.    MyRange.First = 1;
  794.  
  795.    /* Get the first line in this document.
  796.     * If it cannot be found, just set Last=First and skip the
  797.     * proceeding processing.  Otherwise, assume we've seen everything
  798.     * up to but not including the first article in the document.
  799.     */
  800.  
  801.    LockLine(Doc->hFirstBlock,sizeof(TypBlock),0L,&BlockPtr,&LinePtr);
  802.    if(LinePtr->length == END_OF_BLOCK) {
  803.       MyRange.Last = 1;
  804.    } else {
  805.       MyRange.Last = ((TypArticle far *)((char far *)LinePtr+sizeof(TypLine)))->Number - 1;
  806.  
  807.       /* Loop to scan through the document, fabricating article ranges.
  808.        */
  809.       do {
  810.          Art = (TypArticle far *) ((char far *)LinePtr + sizeof(TypLine));
  811.          if(Art->Seen) {
  812.             if(InSeen) {
  813.                /* Continuing a sequence of seen articles.            */
  814.                MyRange.Last = Art->Number;
  815.             } else {
  816.                /* Starting a new sequence of seen articles.          */
  817.                MyRange.First = Art->Number;
  818.                MyRange.Last  = Art->Number;
  819.                InSeen = TRUE;
  820.             }
  821.          } else {
  822.             if(InSeen) {
  823.                /* Ending a sequence of seen articles.                   */
  824.                InSeen = FALSE;
  825.                *(RangePtr++) = MyRange;
  826.                (group->nRanges)++;
  827.             } else {
  828.                /* Continuing a sequence of unseen articles.             */
  829.             }
  830.          }
  831.       } while ((group->nRanges < maxRanges) &&
  832.           NextLine(&BlockPtr,&LinePtr));
  833.  
  834.       if(InSeen) {
  835.          *(RangePtr++) = MyRange;
  836.          (group->nRanges)++;
  837.       }
  838.    }
  839.    GlobalUnlock(BlockPtr->hCurBlock);
  840.  
  841.    MyLength = sizeof(TypLine) + RangeOffset(group->NameLen) +
  842.     sizeof(TypRange)*(group->nRanges) + sizeof(int);
  843.    LocalLinePtr->length = MyLength;
  844.    *(int *) ( (char *)LocalLinePtr + MyLength - sizeof(int) ) = MyLength;
  845.  
  846.    ReplaceLine(LocalLinePtr,&ParentBlock,&ParentLine);
  847.    GlobalUnlock(ParentBlock->hCurBlock);
  848.  
  849.    LocalUnlock(hLine);
  850.    LocalFree(hLine);
  851.  
  852. }
  853.